package com.qch.router;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @Author qch
 * @Date 2021/2/16
 * 1.利用zookeeper的路径感知能力，在服务准备进行重启之前将当前机器的IP地址和应用名写入 zookeeper。
 * 2.服务消费者监听该目录，读取其中需要进行关闭的应用名和机器IP列表并且保存到内存中。
 * 3.当前请求过来时，判断是否是请求该应用，如果是请求重启应用，则将该提供者从服务列表中移除。
 *
 * 编写需要进行预发布的路径管理器，用于缓存和监听所有的待灰度机器信息列表
 * 封装重启机器的实例
 * PathChildrenCacheListener 路径节点监听器
 */
public class ReadyRestartInstances implements PathChildrenCacheListener {
    private static final Logger LOGGER=LoggerFactory.getLogger(ReadyRestartInstances.class);
    private static final String LISTEN_PATHS="/lagou/dubbo/restart/instances";
    private final CuratorFramework zkClient;
    //当节点变化时 给这个集合赋值 重启机器的信息列表
    private volatile Set<String>restartInstances=new HashSet<>();

    private ReadyRestartInstances(CuratorFramework zkClient) {
        this.zkClient = zkClient;
    }
    public static ReadyRestartInstances create()
    {
        final CuratorFramework zookeeperClient=ZookeeperClients.client();
        //检测监听路径是否存在
        try {
            final Stat stat=zookeeperClient.checkExists().forPath(LISTEN_PATHS);
            if(stat==null)
            {
                zookeeperClient.create().creatingParentsIfNeeded().forPath(LISTEN_PATHS);
            }
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("确保基础路径存在");
        }
        final ReadyRestartInstances instances=new ReadyRestartInstances(zookeeperClient);
        //给节点缓存对象加入监听
        //创建一个NodeCache
        PathChildrenCache nodeCache=new PathChildrenCache(zookeeperClient,LISTEN_PATHS,false);
        //给节点缓存对象加入监听
        nodeCache.getListenable().addListener(instances);
        try {
            nodeCache.start();
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("启动路径监听失败");
        }
        return instances;
    }

    /**
     * 根据应用名和主机信息拼一个字符串
     */
    private String buildApplicationAndInstanceStr(String application,String host)
    {
        return application+"_"+host;
    }
    /**
     * 增加重启实例的配置信息方法
     */
    public void addRestartingInstance(String application,String host) throws Exception {
        zkClient.create().creatingParentsIfNeeded().forPath(LISTEN_PATHS+"/"+buildApplicationAndInstanceStr(application, host));

    }
    /**
     * 删除重启实例的配置信息方法
     */
    public void removeRestartingInstance(String application,String host) throws Exception {
        zkClient.delete().forPath(LISTEN_PATHS+"/"+buildApplicationAndInstanceStr(application, host));
    }
    /**
     * 判断节点信息是否存在于restartInstance
     */
    public boolean hasRestartingInstance(String application,String host) {
        return restartInstances.contains(buildApplicationAndInstanceStr(application, host));
    }



    @Override
    public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
        //查询出监听路径下所有的目录配置信息
        final List<String>restartInstances=zkClient.getChildren().forPath(LISTEN_PATHS);
        //给restart
        if(CollectionUtils.isEmpty(restartInstances))
        {
            this.restartInstances= Collections.emptySet();
        }else {
            this.restartInstances=new HashSet<>(restartInstances);
        }
    }
}
